/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package se.sll.engagemangsindex.ejb;

import java.util.Date;
import se.sll.engagemangsindex.entity.Engagement;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import lombok.extern.slf4j.Slf4j;
import se.sll.engagemangsindex.entity.Engagement_;

/**
 *
 * @author khaleddaham
 */
@Stateless
@Slf4j
public class EngagementEjb {

    @PersistenceContext(name = "engagementPU")
    private EntityManager em;

    /**
     * @param obj
     * @return
     */
    public Engagement update(Engagement obj) {
        Timestamp timestamp = this.getTimeStamp();
        if (this.hasEngagement(obj)) {
            log.debug("We have an existing engagement, updating the existing engagement.");
            Engagement old = findByUniqueId(obj);
            old.setClinicalProcessInterestId(obj.getClinicalProcessInterestId());
            old.setOwner(obj.getOwner());
            old.setUpdateTime(timestamp);
            em.merge(old);
        } else {
            // Persist a new engagement.
            obj.setCreationTime(timestamp);
            obj.setUpdateTime(timestamp);
            em.persist(obj);
        }
        em.flush();
        em.clear();
        return obj;
    }

    /**
     *
     * @return
     */
    private Timestamp getTimeStamp() {
        java.util.Date date = new java.util.Date();
        return new Timestamp(date.getTime());
    }

    /**
     *
     * @param registeredIdentification
     * @param serviceDomain
     * @param categorization
     * @param logicalAddress
     * @param businessObjectIdentifier
     */
    public void remove(Engagement e) {
        log.debug("Deleting engagement: " + e.toString());
        e = findByUniqueId(e);
        em.remove(e);
        em.flush();
    }

    /**
     *
     * @param registeredIdentification
     * @param serviceDomain
     * @param categorization
     * @param logicalAddress
     * @param businessObjectIdentifier
     * @return
     */
    public Boolean hasEngagement(Engagement e) {
        Long n = (Long) em.createQuery(
                "SELECT COUNT(e.id) FROM Engagement e WHERE e.registeredResidentIdentification = ?1 AND e.serviceDomain = ?2"
                + " AND e.categorization = ?3"
                + " AND e.logicalAddress = ?4"
                + " AND e.businessObjectInstanceIdentifier = ?5"
                + " AND e.clinicalProcessInterestId = ?6"
                + " AND e.sourceSystem = ?7").setParameter(1, e.getRegisteredResidentIdentification()).
                setParameter(2, e.getServiceDomain()).
                setParameter(3, e.getCategorization()).
                setParameter(4, e.getLogicalAddress()).
                setParameter(5, e.getBusinessObjectInstanceIdentifier()).
                setParameter(6, e.getClinicalProcessInterestId()).
                setParameter(7, e.getSourceSystem()).getSingleResult();
        log.debug("Found {} existing engagements.", n);
        if (n >= 1) {
            return true;
        }
        return false;
    }

    /**
     * Finds one engagement by its business uniqueness.
     *
     * @param registeredIdentification
     * @param serviceDomain
     * @param categorization
     * @param logicalAddress
     * @param businessObjectIdentifier
     * @return
     */
    public Engagement findByUniqueId(Engagement e) {
        return (Engagement) em.createQuery(
                "FROM Engagement e WHERE e.registeredResidentIdentification = ?1"
                + " AND e.serviceDomain = ?2"
                + " AND e.categorization = ?3"
                + " AND e.logicalAddress = ?4"
                + " AND e.businessObjectInstanceIdentifier = ?5"
                + " AND e.clinicalProcessInterestId = ?6"
                + " AND e.sourceSystem = ?7").
                setParameter(1, e.getRegisteredResidentIdentification()).
                setParameter(2, e.getServiceDomain()).
                setParameter(3, e.getCategorization()).
                setParameter(4, e.getLogicalAddress()).
                setParameter(5, e.getBusinessObjectInstanceIdentifier()).
                setParameter(6, e.getClinicalProcessInterestId()).
                setParameter(7, e.getSourceSystem()).getSingleResult();
    }

    /**
     *
     * @return
     */
    public Engagement find(int id) {
        return em.find(Engagement.class, id);
    }

    /**
     *
     * @return
     */
    public List<Engagement> findContent(String registeredResidentIdentification, String serviceDomain,
            String categorization, String logicalAddress, String businessObjectInstanceIdentifier,
            String clinicalProcessInterestId, String mostRecentContent, String sourceSystem, String owner) throws ParseException {
        CriteriaBuilder qb = em.getCriteriaBuilder();
        CriteriaQuery<Engagement> c = qb.createQuery(Engagement.class);
        Root<Engagement> engagement = c.from(Engagement.class);
        List<Predicate> conditions = new ArrayList<Predicate>();

        if (registeredResidentIdentification != null) {
            conditions.add(qb.equal(
                    engagement.get(Engagement_.registeredResidentIdentification), registeredResidentIdentification));
            if (clinicalProcessInterestId != null) {
                conditions.add(qb.equal(
                        engagement.get(Engagement_.clinicalProcessInterestId), clinicalProcessInterestId));
            }
        }

        if (serviceDomain != null) {
            conditions.add(qb.equal(
                    engagement.get(Engagement_.serviceDomain), serviceDomain));
            if (categorization != null) {
                conditions.add(qb.equal(
                        engagement.get(Engagement_.categorization), categorization));
            }
            if (logicalAddress != null) {
                conditions.add(qb.equal(
                        engagement.get(Engagement_.logicalAddress), logicalAddress));
            }
            if (businessObjectInstanceIdentifier != null) {
                conditions.add(qb.equal(
                        engagement.get(Engagement_.businessObjectInstanceIdentifier), businessObjectInstanceIdentifier));
            }
            if (mostRecentContent != null) {
                SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
                format.parse(mostRecentContent);
                Date date = format.getCalendar().getTime();
                Timestamp timestamp = new Timestamp(date.getTime());
                conditions.add(qb.greaterThan(
                        engagement.get(Engagement_.updateTime), timestamp));
            }
        }

        if (owner != null) {
            conditions.add(qb.equal(
                    engagement.get(Engagement_.owner), owner));
        }

        c.where(conditions.toArray(new Predicate[conditions.size()]));
        TypedQuery<Engagement> q = em.createQuery(c);
        return q.getResultList();
    }

    /**
     *
     * @return
     */
    public Collection<Engagement> findAll() {
        Query query = em.createQuery("SELECT e FROM Engagement e");
        return (Collection<Engagement>) query.getResultList();
    }

    /**
     *
     * @param registeredId
     */
    public Collection<Engagement> findByRegisteredId(String registeredId) {
        return (Collection<Engagement>) em.createQuery("FROM Engagement e WHERE e.registeredResidentIdentification = ?1").setParameter(1, registeredId).getResultList();
    }

    /**
     *
     * @param registeredId
     * @param category
     */
    public Collection<Engagement> findByRegisteredIdAndCategory(String registeredId, String category) {
        return (Collection<Engagement>) em.createQuery("FROM Engagement e WHERE e.registeredResidentIdentification = ?1 AND e.categorization = ?2").setParameter(1, registeredId).setParameter(2, category).getResultList();
    }

    public boolean isOwnerSame(String owner, Engagement e) {
        if (owner.equals(e.getOwner())) {
            return true;
        }
        return false;
    }

    /**
     * The purpose of this method is to log a warning if an engagement has a
     * different owner than one that has already been persisted.
     *
     * @param registeredResidentIdentification
     * @param serviceDomain
     * @param categorization
     * @param logicalAddress
     * @param businessObjectInstanceIdentifier
     */
    public boolean hasExistingEngagementWithDifferentOwner(Engagement e) {
        if (this.hasEngagement(e)) {
            Engagement existing = this.findByUniqueId(e);
            return existing.getOwner().equals(e.getOwner());
        }
        return false;

    }

    /**
     * Do a simple SELECT COUNT(e.id) FROM Engagement LIMIT 1 to test database
     * connection.
     */
    public void pingDatabase() {
        Query query = em.createQuery("SELECT COUNT(e.id) FROM Engagement e");
        query.setMaxResults(1);
        query.getResultList();
    }
}